package main

import (
	//	"fmt"
	//	"io"
	"log"
	"net/http"
	//"strings"
	"time"

	"golang.org/x/net/websocket"
)

const (
	TEXT_MTYPE   = "text_mtype"
	STATUS_MTYPE = "status_mtype"
	TIME_FORMAT  = "01-02 15:04:05"
)

var runningActiveRoom *ActiveRoom = &ActiveRoom{}

type ActiveRoom struct {
	OnlineUsers map[string]*OnlineUser //users
	Broadcast   chan Message
	CloseSign   chan bool
}

type OnlineUser struct {
	InRoom     *ActiveRoom
	Connection *websocket.Conn
	UserInfo   *User
	Send       chan Message
}

type User struct {
	Name string
}

//定义消息的
type Message struct {
	MType       string //消息类型,ws链接/关闭,还是发送消息
	TextMessage TextMessage
	UserStatus  UserStatus
}

//消息内容
type TextMessage struct {
	Content  string //消息的文本内容
	UserInfo *User  //是谁触发的
	Time     string //消息的时间
}

//用户列表
type UserStatus struct {
	Users []*User
}

func InitChatRoom() {
	runningActiveRoom = &ActiveRoom{
		OnlineUsers: make(map[string]*OnlineUser), //在线用户的列表
		Broadcast:   make(chan Message),           //广播的信号
		CloseSign:   make(chan bool),              //关闭了ws的信号
	}
	go runningActiveRoom.run()
}

// Core function of room
func (this *ActiveRoom) run() {
	for {
		select {
		case b := <-this.Broadcast:
			for _, online := range this.OnlineUsers {
				online.Send <- b
			}
		case c := <-this.CloseSign:
			if c == true {
				close(this.Broadcast)
				close(this.CloseSign)
				return
			}
		}
	}
}

func (this *OnlineUser) PullFromClient() {
	for {
		var content string
		err := websocket.Message.Receive(this.Connection, &content)
		// If user closes or refreshes the browser, a err will occur
		if err != nil {
			return
		}

		m := Message{
			MType: TEXT_MTYPE,
			TextMessage: TextMessage{
				UserInfo: this.UserInfo,
				Time:     humanCreatedAt(),
				Content:  content,
			},
		}
		//每次收到消息就推送
		log.Println(m)

		//发送消息到广播通道
		this.InRoom.Broadcast <- m
	}
}

//发送消息到客户端
func (this *OnlineUser) PushToClient() {
	for b := range this.Send {
		err := websocket.JSON.Send(this.Connection, b)
		if err != nil {
			break
		}
	}
}

//ws关闭之后释放资源
func (this *OnlineUser) killUserResource() {
	this.Connection.Close()
	delete(this.InRoom.OnlineUsers, this.UserInfo.Name)
	close(this.Send)

	m := Message{
		MType: STATUS_MTYPE,
		UserStatus: UserStatus{
			Users: runningActiveRoom.GetOnlineUsers(),
		},
	}
	runningActiveRoom.Broadcast <- m
}

//获取所有的在线用户
func (this *ActiveRoom) GetOnlineUsers() (users []*User) {
	for _, online := range this.OnlineUsers {
		users = append(users, online.UserInfo)
	}
	return
}

//发送消息到所有的用户
func (this *ActiveRoom) BroadcastToOnlineUsers(content string) {

	m := Message{
		MType: TEXT_MTYPE,
		TextMessage: TextMessage{
			Time:    humanCreatedAt(),
			Content: content,
		},
	}
	//每次收到消息就推送
	log.Println(m)

	//发送消息到广播通道
	this.Broadcast <- m
}

//格式化时间的
func humanCreatedAt() string {
	return time.Now().Format(TIME_FORMAT)
}

func BuildConnection(ws *websocket.Conn) {
	name := ws.Request().URL.Query().Get("id")

	if name == "" {
		return
	}

	log.Println(name)

	onlineUser := &OnlineUser{
		InRoom:     runningActiveRoom,
		Connection: ws,
		Send:       make(chan Message, 256),
		UserInfo: &User{
			Name: name,
		},
	}
	runningActiveRoom.OnlineUsers[name] = onlineUser

	m := Message{
		MType: STATUS_MTYPE,
		UserStatus: UserStatus{
			Users: runningActiveRoom.GetOnlineUsers(),
		},
	}
	//把当前的用户加入在线列表中
	log.Println(m)

	runningActiveRoom.Broadcast <- m

	//这个是自己给自己的
	go onlineUser.PushToClient()
	//这个是等待接收的
	onlineUser.PullFromClient()

	onlineUser.killUserResource()
}

//通过http发送消息到ws
func Say(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("Receive"))
	w.Write([]byte("<br/>"))

	var content string

	content = req.URL.Query().Get("msg")
	w.Write([]byte(content))
	//content = "console.log(123456)"
	runningActiveRoom.BroadcastToOnlineUsers(content)

	w.Write([]byte("<br/>"))
	w.Write([]byte("Send WS"))
}

//
func main() {

	InitChatRoom()

	http.Handle("/echo", websocket.Handler(BuildConnection))
	http.Handle("/Say", http.HandlerFunc(Say))
	http.Handle("/", http.FileServer(http.Dir(".")))

	err := http.ListenAndServe(":8080", nil)

	if err != nil {
		panic("ListenAndServe: " + err.Error())
	}
}
